/*
  +----------------------------------------------------------------------+
  | PHP Version 5                                                        |
  +----------------------------------------------------------------------+
  | Copyright (c) 1997-2013 The PHP Group                                |
  +----------------------------------------------------------------------+
  | This source file is subject to version 3.01 of the PHP license,      |
  | that is bundled with this package in the file LICENSE, and is        |
  | available through the world-wide-web at the following url:           |
  | http://www.php.net/license/3_01.txt                                  |
  | If you did not receive a copy of the PHP license and are unable to   |
  | obtain it through the world-wide-web, please send a note to          |
  | license@php.net so we can mail you a copy immediately.               |
  +----------------------------------------------------------------------+
  | Author:                                                              |
  +----------------------------------------------------------------------+
*/

/* $Id: ext_view.c */
/* 模板引擎ext_view_impl接口与ext_view实现类 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "php.h"
#include "php_ini.h"
#include "main/SAPI.h"
#include "Zend/zend_interfaces.h"
#include "ext/standard/info.h"
#include "ext/standard/php_var.h"
#include "ext/standard/php_string.h"
#include "ext/standard/php_smart_str.h"
#include "ext/standard/url.h"
#include "ext/pcre/php_pcre.h"
#include "php_ext_framework.h"
#include "ext_fetch_url.h"
#include <curl/curl.h>

zend_class_entry *ext_fetch_url_ce;

ZEND_BEGIN_ARG_INFO_EX(void_arginfo, 0, 0, 0)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(fetch_arginfo, 0, 0, 1)
	ZEND_ARG_INFO(0, url)
	ZEND_ARG_INFO(0, callback)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(responseCookies_arginfo, 0, 0, 0)
	ZEND_ARG_INFO(0, all)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(responseHeaders_arginfo, 0, 0, 0)
	ZEND_ARG_INFO(0, parse)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(setAllowRedirect_arginfo, 0, 0, 0)
	ZEND_ARG_INFO(0, allow)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(setConnectTimeout_arginfo, 0, 0, 0)
	ZEND_ARG_INFO(0, ms)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(setCookie_arginfo, 0, 0, 2)
	ZEND_ARG_INFO(0, name)
	ZEND_ARG_INFO(0, value)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(setCookies_arginfo, 0, 0, 1)
	ZEND_ARG_INFO(0, cookies)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(setHeader_arginfo, 0, 0, 2)
	ZEND_ARG_INFO(0, name)
	ZEND_ARG_INFO(0, value)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(setMethod_arginfo, 0, 0, 1)
	ZEND_ARG_INFO(0, method)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(setPostData, 0, 0, 1)
	ZEND_ARG_INFO(0, post_data)
	ZEND_ARG_INFO(0, multil)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(setReadTimeout_arginfo, 0, 0, 0)
	ZEND_ARG_INFO(0, ms)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(setBinary_arginfo, 0, 0, 3)
	ZEND_ARG_INFO(0, post_filed)
	ZEND_ARG_INFO(0, uploadfile_name)
	ZEND_ARG_INFO(0, url)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(setFile_arginfo, 0, 0, 2)
	ZEND_ARG_INFO(0, post_filed)
	ZEND_ARG_INFO(0, path)
ZEND_END_ARG_INFO()

ZEND_METHOD(ext_fetch_url, __construct){

}

ZEND_METHOD(ext_fetch_url, setBinary){
	zval *input_filed_name, *binary_data, *uploadfile_name;
	zval *g_binary_data, *item_data;

	if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zzz", &input_filed_name, &uploadfile_name, &binary_data) == FAILURE){
		RETURN_FALSE;
	}

	if(Z_TYPE_P(input_filed_name) != IS_STRING || Z_TYPE_P(uploadfile_name) != IS_STRING || Z_TYPE_P(binary_data) != IS_STRING){
		RETURN_FALSE;
	}

	g_binary_data = zend_read_property(FETCH_THIS, ZEND_STRL("binary_data"), 0 TSRMLS_CC);

	if(Z_TYPE_P(g_binary_data) == IS_NULL){
		MAKE_STD_ZVAL(g_binary_data);
		array_init(g_binary_data);
	}

	MAKE_STD_ZVAL(item_data);
	array_init(item_data);

	add_index_stringl(item_data, 0, Z_STRVAL_P(uploadfile_name), Z_STRLEN_P(uploadfile_name), 1);
	add_index_stringl(item_data, 1, Z_STRVAL_P(binary_data), Z_STRLEN_P(binary_data), 1);

	add_assoc_zval(g_binary_data, Z_STRVAL_P(input_filed_name), item_data);

	zend_update_property(FETCH_THIS, ZEND_STRL("binary_data"), g_binary_data TSRMLS_CC);
}

ZEND_METHOD(ext_fetch_url, setFile){
	zval *file_path, *input_filed_name;
	zval *upload_filepaths;

	if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz", &input_filed_name, &file_path) == FAILURE){
		RETURN_FALSE;
	}

	if(Z_TYPE_P(file_path) != IS_STRING || Z_TYPE_P(input_filed_name) != IS_STRING){
		RETURN_FALSE;
	}

	upload_filepaths = zend_read_property(FETCH_THIS, ZEND_STRL("upload_filepaths"), 0 TSRMLS_CC);

	if(Z_TYPE_P(upload_filepaths) == IS_NULL){
		MAKE_STD_ZVAL(upload_filepaths);
		array_init(upload_filepaths);
	}

	add_assoc_stringl(upload_filepaths, Z_STRVAL_P(input_filed_name), Z_STRVAL_P(file_path), Z_STRLEN_P(file_path), 1);

	zend_update_property(FETCH_THIS, ZEND_STRL("upload_filepaths"), upload_filepaths TSRMLS_CC);
}

ZEND_METHOD(ext_fetch_url, body){
	zval *zval_body;

	zval_body = zend_read_property(FETCH_THIS, ZEND_STRL("body"), 0 TSRMLS_CC);

	RETURN_STRINGL(Z_STRVAL_P(zval_body), Z_STRLEN_P(zval_body), 1);
}

ZEND_METHOD(ext_fetch_url, clean){
	zend_update_property_stringl(FETCH_THIS, ZEND_STRL("body"), ZEND_STRL("") TSRMLS_CC);
	zend_update_property_stringl(FETCH_THIS, ZEND_STRL("errmsg"), ZEND_STRL("") TSRMLS_CC);
	zend_update_property_long(FETCH_THIS, ZEND_STRL("errno"), 0 TSRMLS_CC);
	zend_update_property_null(FETCH_THIS, ZEND_STRL("httpCode") TSRMLS_CC);
	zend_update_property_stringl(FETCH_THIS, ZEND_STRL("cookies"), ZEND_STRL("") TSRMLS_CC);
	zend_update_property_stringl(FETCH_THIS, ZEND_STRL("headers"), ZEND_STRL("") TSRMLS_CC);
	zend_update_property_stringl(FETCH_THIS, ZEND_STRL("send_headers"), ZEND_STRL("") TSRMLS_CC);
	zend_update_property_stringl(FETCH_THIS, ZEND_STRL("data"), ZEND_STRL("") TSRMLS_CC);
	zend_update_property_null(FETCH_THIS, ZEND_STRL("binary_data") TSRMLS_CC);
	zend_update_property_null(FETCH_THIS, ZEND_STRL("upload_filepaths") TSRMLS_CC);
	zend_update_property_stringl(FETCH_THIS, ZEND_STRL("method"), ZEND_STRL("get") TSRMLS_CC);
}

ZEND_METHOD(ext_fetch_url, errmsg){
	zval *errmsg = zend_read_property(FETCH_THIS, ZEND_STRL("errmsg"), 0 TSRMLS_CC);

	RETURN_STRINGL(Z_STRVAL_P(errmsg), Z_STRLEN_P(errmsg), 1);
}

ZEND_METHOD(ext_fetch_url, errcode){
	zval *err_no = zend_read_property(FETCH_THIS, ZEND_STRL("errno"), 0 TSRMLS_CC);

	RETURN_LONG(Z_LVAL_P(err_no));
}

static size_t read_data(void *buffer, size_t size, size_t nmemb, void *data){
	smart_str *content = (smart_str*)data;

	smart_str_appendl(content, buffer, size*nmemb);

	return size*nmemb;
}

ZEND_METHOD(ext_fetch_url, fetch){
	CURLcode return_code;
	CURL *curl_handler;
	struct curl_slist *http_headers = NULL;
	zval *url, *callback, *cookies, *connect_timeout, *allow_redirect, *method, *post_data, *read_timeout,
		 *send_headers, *err_no, *errmsg, *binary_data, *upload_filepaths;
	smart_str body_str = {0}, header_str = {0};
	struct curl_httppost *post = NULL;
	struct curl_httppost *last = NULL;

	if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|z", &url, &callback) == FAILURE){
		RETURN_FALSE;
	}

	err_no = zend_read_property(FETCH_THIS, ZEND_STRL("errno"), 0 TSRMLS_CC);
	errmsg  = zend_read_property(FETCH_THIS, ZEND_STRL("errmsg"), 0 TSRMLS_CC);

	if(Z_LVAL_P(err_no) > 0){
		php_printf("errno:%d, errmsg:%s", Z_LVAL_P(err_no), Z_STRVAL_P(errmsg));
		RETURN_FALSE;
	}

	if(Z_TYPE_P(url) != IS_STRING){
		FETCH_ERROR("fetch url must be string.", 500);
		RETURN_FALSE;
	}

	return_code = curl_global_init(FETCH_CURL_MODE);

	if(return_code != CURLE_OK){
		curl_global_cleanup();
		FETCH_ERROR("curl init failed.", 500);
		RETURN_FALSE;
	}

	curl_handler = curl_easy_init();

	if(NULL == curl_handler){
		curl_easy_cleanup(curl_handler);
		curl_global_cleanup();
		FETCH_ERROR("get curl handler failed.", 500);
		RETURN_FALSE;
	}

	cookies = zend_read_property(FETCH_THIS, ZEND_STRL("cookies"), 0 TSRMLS_CC);
	connect_timeout = zend_read_property(FETCH_THIS, ZEND_STRL("connect_timeout"), 0 TSRMLS_CC);
	read_timeout = zend_read_property(FETCH_THIS, ZEND_STRL("read_timeout"), 0 TSRMLS_CC);
	allow_redirect = zend_read_property(FETCH_THIS, ZEND_STRL("allow_redirect"), 0 TSRMLS_CC);
	method = zend_read_property(FETCH_THIS, ZEND_STRL("method"), 0 TSRMLS_CC);
	post_data = zend_read_property(FETCH_THIS, ZEND_STRL("data"), 0 TSRMLS_CC);
	send_headers = zend_read_property(FETCH_THIS, ZEND_STRL("send_headers"), 0 TSRMLS_CC);
	binary_data = zend_read_property(FETCH_THIS, ZEND_STRL("binary_data"), 0 TSRMLS_CC);
	upload_filepaths = zend_read_property(FETCH_THIS, ZEND_STRL("upload_filepaths"), 0 TSRMLS_CC);


	curl_easy_setopt(curl_handler, CURLOPT_URL, Z_STRVAL_P(url));
	curl_easy_setopt(curl_handler, CURLOPT_COOKIE, Z_STRVAL_P(cookies));
	curl_easy_setopt(curl_handler, CURLOPT_WRITEFUNCTION, &read_data);
	curl_easy_setopt(curl_handler, CURLOPT_WRITEDATA, &body_str);
	curl_easy_setopt(curl_handler, CURLOPT_HEADERDATA, &header_str);
	curl_easy_setopt(curl_handler, CURLOPT_HEADERFUNCTION, &read_data);
	curl_easy_setopt(curl_handler, CURLOPT_TIMEOUT, Z_LVAL_P(read_timeout));
	curl_easy_setopt(curl_handler, CURLOPT_CONNECTTIMEOUT, Z_LVAL_P(connect_timeout));
	curl_easy_setopt(curl_handler, CURLOPT_AUTOREFERER, Z_LVAL_P(allow_redirect));
	curl_easy_setopt(curl_handler, CURLOPT_MAXREDIRS, 5);

	if(strcmp(Z_STRVAL_P(method), "get") == 0){
		curl_easy_setopt(curl_handler, CURLOPT_HTTPGET, 1);
	}else{
		if(Z_TYPE_P(binary_data) != IS_NULL || Z_TYPE_P(upload_filepaths) != IS_NULL){
			zval *delim, *post_arr, *delim_equal;

			MAKE_STD_ZVAL(delim_equal);
			MAKE_STD_ZVAL(delim);
			MAKE_STD_ZVAL(post_arr);

			ZVAL_STRING(delim, "&", 1);
			ZVAL_STRING(delim_equal, "=", 1);
			array_init(post_arr);
			php_explode(delim, post_data, post_arr, LONG_MAX);

			for(zend_hash_internal_pointer_reset(Z_ARRVAL_P(post_arr));
				zend_hash_has_more_elements(Z_ARRVAL_P(post_arr)) == SUCCESS;
				zend_hash_move_forward(Z_ARRVAL_P(post_arr))){
				zval **data_str;
				zval *temp_data, **post_data_name, **post_data_value, *temp_zval;
				if(zend_hash_get_current_data(Z_ARRVAL_P(post_arr), (void**)&data_str) == FAILURE){
					continue;
				}

				if(Z_STRLEN_PP(data_str) > 0){
					MAKE_STD_ZVAL(temp_data);
					array_init(temp_data);
					temp_zval = *data_str;

					php_explode(delim_equal, temp_zval, temp_data, LONG_MAX);
					
					zend_hash_index_find(Z_ARRVAL_P(temp_data), 0, (void**)&post_data_name);
					zend_hash_index_find(Z_ARRVAL_P(temp_data), 1, (void**)&post_data_value);

					curl_formadd(&post, &last, CURLFORM_COPYNAME, Z_STRVAL_PP(post_data_name), CURLFORM_COPYCONTENTS, Z_STRVAL_PP(post_data_value), CURLFORM_END);

					zval_dtor(temp_data);
				}
			}

			zval_dtor(post_arr);
			zval_dtor(delim);
			zval_dtor(delim_equal);

			if(Z_TYPE_P(binary_data) != IS_NULL)
			for(zend_hash_internal_pointer_reset(Z_ARRVAL_P(binary_data));
				zend_hash_has_more_elements(Z_ARRVAL_P(binary_data)) == SUCCESS;
				zend_hash_move_forward(Z_ARRVAL_P(binary_data))){
				char *input_file_name;
				uint input_file_name_len;
				ulong idx;
				zval **item_data;
				zval **upload_binary_data;
				zval **uploadfile_name;

				if(zend_hash_get_current_key_ex(Z_ARRVAL_P(binary_data), &input_file_name, &input_file_name_len, &idx, 0, NULL) != HASH_KEY_IS_STRING){
					continue;
				}

				if(zend_hash_get_current_data(Z_ARRVAL_P(binary_data), (void**)&item_data) == FAILURE){
					continue;
				} 

				zend_hash_index_find(Z_ARRVAL_PP(item_data), 0, (void**)&uploadfile_name);
				zend_hash_index_find(Z_ARRVAL_PP(item_data), 1, (void**)&upload_binary_data);

				curl_formadd(&post, 
							&last, 
							CURLFORM_COPYNAME, 
							input_file_name, 
							CURLFORM_BUFFER, 
							Z_STRVAL_PP(uploadfile_name), //todo:setBinary需要传递文件名参数
							CURLFORM_BUFFERPTR,
							Z_STRVAL_PP(upload_binary_data), 
							CURLFORM_BUFFERLENGTH, 
							Z_STRLEN_PP(upload_binary_data), 
							CURLFORM_END
				);
			}

			if(Z_TYPE_P(upload_filepaths) != IS_NULL)
			for(zend_hash_internal_pointer_reset(Z_ARRVAL_P(upload_filepaths));
				zend_hash_has_more_elements(Z_ARRVAL_P(upload_filepaths)) == SUCCESS;
				zend_hash_move_forward(Z_ARRVAL_P(upload_filepaths))){
				char *input_filed_name;
				uint input_file_name_len;
				ulong idx;
				zval **file_path;

				if(zend_hash_get_current_key_ex(Z_ARRVAL_P(upload_filepaths), &input_filed_name, &input_file_name_len, &idx, 0, NULL) != HASH_KEY_IS_STRING){
					continue;
				}

				if(zend_hash_get_current_data(Z_ARRVAL_P(upload_filepaths), (void**)&file_path) == FAILURE){
					continue;
				}

				curl_formadd(&post, &last, CURLFORM_COPYNAME, input_filed_name, CURLFORM_FILE, Z_STRVAL_PP(file_path), CURLFORM_END);
			}

			curl_easy_setopt(curl_handler, CURLOPT_HTTPPOST, post);
			http_headers = curl_slist_append(http_headers, estrdup("Expect:"));//防止出现HTTP 100跳转
		}else{
			curl_easy_setopt(curl_handler, CURLOPT_POSTFIELDS, Z_STRVAL_P(post_data));
			curl_easy_setopt(curl_handler, CURLOPT_POST, 1);
		}
	}

	if(Z_TYPE_P(send_headers) == IS_ARRAY && zend_hash_num_elements(Z_ARRVAL_P(send_headers)) > 0){

		for(zend_hash_internal_pointer_reset(Z_ARRVAL_P(send_headers));
			zend_hash_has_more_elements(Z_ARRVAL_P(send_headers)) == SUCCESS;
			zend_hash_move_forward(Z_ARRVAL_P(send_headers))){
			char *header_key;
			uint header_keylen;
			ulong idx;
			zval **header_val;
			smart_str impl_headers = {0};

			if(zend_hash_get_current_key_ex(Z_ARRVAL_P(send_headers), &header_key, &header_keylen, &idx, 0, NULL) != HASH_KEY_IS_STRING){
				continue;
			}

			if(zend_hash_get_current_data(Z_ARRVAL_P(send_headers), (void**)&header_val) == FAILURE){
				continue;
			}

			smart_str_appendl(&impl_headers, header_key, header_keylen);
			smart_str_appendl(&impl_headers, ": ", 2);
			smart_str_appendl(&impl_headers, Z_STRVAL_PP(header_val), Z_STRLEN_PP(header_val));
			http_headers = curl_slist_append(http_headers, impl_headers.c);
		}
	}

	curl_easy_setopt(curl_handler, CURLOPT_HTTPHEADER, http_headers);

	curl_easy_perform(curl_handler);

	curl_slist_free_all(http_headers);
	curl_formfree(post);
	curl_easy_cleanup(curl_handler);
	curl_global_cleanup();

	smart_str_0(&body_str);
	smart_str_0(&header_str);

	zend_update_property_stringl(FETCH_THIS, ZEND_STRL("headers"), header_str.c, header_str.len TSRMLS_CC);
	zend_update_property_stringl(FETCH_THIS, ZEND_STRL("body"), body_str.c, body_str.len TSRMLS_CC);
}

ZEND_METHOD(ext_fetch_url, httpCode){
	pcre_cache_entry *pce;
	zval *headers;
	zval *result_match, *match_long, **http_code;
	char *regex = estrdup("/^HTTP\\/1\\.1\\s(.*)\\sOK/");

	if((pce = pcre_get_compiled_regex_cache(regex, strlen(regex) TSRMLS_CC)) == NULL){
		RETURN_FALSE;
	}

	MAKE_STD_ZVAL(result_match);
	MAKE_STD_ZVAL(match_long);

	headers = zend_read_property(FETCH_THIS, ZEND_STRL("headers"), 0 TSRMLS_CC);

	php_pcre_match_impl(pce, Z_STRVAL_P(headers), Z_STRLEN_P(headers), match_long, result_match, 0, 0, 0, 0 TSRMLS_CC);

	if(Z_LVAL_P(match_long) > 0){
		if(zend_hash_index_find(Z_ARRVAL_P(result_match), 1, (void**)&http_code) == FAILURE){
			RETURN_FALSE;
		}else{
			RETURN_STRINGL(Z_STRVAL_PP(http_code), Z_STRLEN_PP(http_code), 0);
		}
	}else{
		RETURN_FALSE;
	}
}

ZEND_METHOD(ext_fetch_url, responseCookies){
	pcre_cache_entry *pce;
	zval *headers, *result_match, *match_long;
	char *regex = estrdup("/Set-Cookie:\\s(.*?);/");

	headers = zend_read_property(FETCH_THIS, ZEND_STRL("headers"), 0 TSRMLS_CC);

	array_init(return_value);

	if(Z_STRLEN_P(headers) > 0){
		if((pce = pcre_get_compiled_regex_cache(regex, strlen(regex) TSRMLS_CC)) == NULL){
			RETURN_NULL();
		}

		MAKE_STD_ZVAL(result_match);
		MAKE_STD_ZVAL(match_long);

		//void php_pcre_match_impl(pcre_cache_entry *pce, char *subject, int subject_len, zval *return_value, zval *subpats, int global, int use_flags, long flags, long start_offset TSRMLS_DC)
		php_pcre_match_impl(pce, Z_STRVAL_P(headers), Z_STRLEN_P(headers), match_long, result_match, 1, 0, 0, 0 TSRMLS_CC);
		
		if(Z_LVAL_P(match_long) > 0){
			zval **result;
			HashTable *result_ht;
			char *found = NULL;
			long found_offset;
			char *cookie_name;
			char *cookie_value;

			if(zend_hash_index_find(Z_ARRVAL_P(result_match), 1, (void**)&result) != FAILURE){
				result_ht = Z_ARRVAL_PP(result);

				for(zend_hash_internal_pointer_reset(result_ht);
					zend_hash_has_more_elements(result_ht) == SUCCESS;
					zend_hash_move_forward(result_ht)){
					zval **tmpzval;

					if(zend_hash_get_current_data(result_ht, (void**)&tmpzval) == FAILURE){
						continue;
					}

					found = php_memnstr(Z_STRVAL_PP(tmpzval), "=", 1, Z_STRVAL_PP(tmpzval) + Z_STRLEN_PP(tmpzval));
					found_offset = found - Z_STRVAL_PP(tmpzval);

					cookie_name = estrndup(Z_STRVAL_PP(tmpzval), found_offset);
					cookie_value= estrndup(found+1, strlen(found)-1);

					add_assoc_stringl(return_value, cookie_name, cookie_value, strlen(cookie_value), 1);

					efree(cookie_name);
					efree(cookie_value);
				}
			}
		}
	}else{
		RETURN_NULL();
	}
}

ZEND_METHOD(ext_fetch_url, responseHeaders){
	zval *headers, *delim;
	uint idx;

	headers = zend_read_property(FETCH_THIS, ZEND_STRL("headers"), 0 TSRMLS_CC);

	MAKE_STD_ZVAL(delim);

	array_init(return_value);

	ZVAL_STRING(delim, "\r\n", 1);

	php_explode(delim, headers, return_value, LONG_MAX);

	idx = zend_hash_num_elements(Z_ARRVAL_P(return_value));

	zend_hash_index_del(Z_ARRVAL_P(return_value), idx-1);

	zend_hash_index_del(Z_ARRVAL_P(return_value), idx-2);

	zval_dtor(delim);
}

ZEND_METHOD(ext_fetch_url, setAllowRedirect){
	zval *allow;

	if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &allow) == FAILURE){
		RETURN_FALSE;
	}

	convert_to_long(allow);

	zend_update_property_long(FETCH_THIS, ZEND_STRL("allow_redirect"), Z_LVAL_P(allow) TSRMLS_CC);
}

ZEND_METHOD(ext_fetch_url, setConnectTimeout){
	zval *connect_timeout;

	if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &connect_timeout) == FAILURE){
		RETURN_FALSE;
	}

	convert_to_long(connect_timeout);

	zend_update_property_long(FETCH_THIS, ZEND_STRL("connect_timeout"), Z_LVAL_P(connect_timeout) TSRMLS_CC);
}

ZEND_METHOD(ext_fetch_url, setCookie){
	zval *zval_cookies;
	zval *cookie_name, *cookie_value;
	smart_str impl_cookies = {0};

	if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz", &cookie_name, &cookie_value) == FAILURE){
		RETURN_FALSE;
	}

	if(Z_TYPE_P(cookie_name) != IS_STRING || Z_TYPE_P(cookie_value) != IS_STRING){
		RETURN_FALSE;
	}

	zval_cookies = zend_read_property(FETCH_THIS, ZEND_STRL("cookies"), 0 TSRMLS_CC);

	smart_str_appendl(&impl_cookies, Z_STRVAL_P(zval_cookies), Z_STRLEN_P(zval_cookies));
	smart_str_appendl(&impl_cookies, Z_STRVAL_P(cookie_name), Z_STRLEN_P(cookie_name));
	smart_str_appendc(&impl_cookies, '=');
	smart_str_appendl(&impl_cookies, Z_STRVAL_P(cookie_value), Z_STRLEN_P(cookie_value));
	smart_str_appendc(&impl_cookies, ';');
	smart_str_0(&impl_cookies);

	zend_update_property_stringl(FETCH_THIS, ZEND_STRL("cookies"), impl_cookies.c, impl_cookies.len TSRMLS_CC);
}

ZEND_METHOD(ext_fetch_url, setCookies){
	zval *zval_cookies;
	zval *cookie_array;
	smart_str impl_cookies = {0};
	HashTable *cookies_ht;

	if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &cookie_array) == FAILURE){
		RETURN_FALSE;
	}

	if(Z_TYPE_P(cookie_array) != IS_ARRAY){
		RETURN_FALSE;
	}

	zval_cookies = zend_read_property(FETCH_THIS, ZEND_STRL("cookies"), 0 TSRMLS_CC);
	cookies_ht = Z_ARRVAL_P(cookie_array);

	smart_str_appendl(&impl_cookies, Z_STRVAL_P(zval_cookies), Z_STRLEN_P(zval_cookies));

	for(zend_hash_internal_pointer_reset(cookies_ht);
		zend_hash_has_more_elements(cookies_ht) == SUCCESS;
		zend_hash_move_forward(cookies_ht))
	{
		zval **value;
		char *key;
		uint key_len;
		ulong idx;

		if(zend_hash_get_current_key_ex(cookies_ht, &key, &key_len, &idx, 0, NULL) != HASH_KEY_IS_STRING){
			continue;
		}

		if(zend_hash_get_current_data(cookies_ht, (void**)&value) == FAILURE){
			continue;
		}

		convert_to_string(*value);

		if(Z_TYPE_PP(value) != IS_STRING){
			continue;
		}

		smart_str_appendl(&impl_cookies, key, key_len-1);
		smart_str_appendl(&impl_cookies, "=", 1);
		smart_str_appendl(&impl_cookies, Z_STRVAL_PP(value), Z_STRLEN_PP(value));
		smart_str_appendl(&impl_cookies, ";", 1);
	}

	php_url_decode(impl_cookies.c, impl_cookies.len);
	smart_str_0(&impl_cookies);
	zend_update_property_stringl(FETCH_THIS, ZEND_STRL("cookies"), impl_cookies.c, impl_cookies.len TSRMLS_CC);
}

ZEND_METHOD(ext_fetch_url, setHeader){
	zval *headers, *value=NULL, *send_headers;
	HashTable *headers_ht;

	if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|z", &headers) == FAILURE){
		RETURN_FALSE;
	}

	send_headers = zend_read_property(FETCH_THIS, ZEND_STRL("send_headers"), 0 TSRMLS_CC);

	if(Z_TYPE_P(send_headers) == IS_NULL){
		MAKE_STD_ZVAL(send_headers);
		array_init(send_headers);
	}

	headers_ht = Z_ARRVAL_P(send_headers);

	if(Z_TYPE_P(headers) == IS_ARRAY){
		for(zend_hash_internal_pointer_reset(Z_ARRVAL_P(headers));
			zend_hash_has_more_elements(Z_ARRVAL_P(headers)) == SUCCESS;
			zend_hash_move_forward(Z_ARRVAL_P(headers))){
			char* key;
			uint key_len;
			ulong idx;
			zval **tmpzval;

			if(zend_hash_get_current_key_ex(Z_ARRVAL_P(headers), &key, &key_len, &idx, 0, NULL) != HASH_KEY_IS_STRING){
				continue;
			}

			if(zend_hash_get_current_data(Z_ARRVAL_P(headers), (void**)&tmpzval) == FAILURE){
				continue;
			}

			add_assoc_stringl(send_headers, key, Z_STRVAL_PP(tmpzval), Z_STRLEN_PP(tmpzval), 1);
		}
	}else if(Z_TYPE_P(headers) == IS_STRING && Z_TYPE_P(value) == IS_STRING){
		add_assoc_stringl(send_headers, Z_STRVAL_P(headers), Z_STRVAL_P(value), Z_STRLEN_P(value), 1);
	}else{
		zend_error(E_WARNING, "param error.");
	}

	zend_update_property(FETCH_THIS, ZEND_STRL("send_headers"), send_headers);
}

ZEND_METHOD(ext_fetch_url, setMethod){
	zval *zval_method;
	char *method;

	if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zval_method) == FAILURE){
		RETURN_FALSE;
	}

	if(Z_TYPE_P(zval_method) != IS_STRING){
		RETURN_FALSE;
	}

	method = php_strtolower(Z_STRVAL_P(zval_method), Z_STRLEN_P(zval_method));

	if(strcmp(method, "get") == 0){
		zend_update_property_stringl(FETCH_THIS, ZEND_STRL("method"), ZEND_STRL("get") TSRMLS_CC);
	}else if(strcmp(method, "post") == 0){
		zend_update_property_stringl(FETCH_THIS, ZEND_STRL("method"), ZEND_STRL("post") TSRMLS_CC);
	}else{
		FETCH_ERROR("Not support method.", 404);
		RETURN_FALSE;
	}

	RETURN_TRUE;
}

ZEND_METHOD(ext_fetch_url, setPostData){
	zval *data, *post_data;
	HashTable *post_data_ht;
	smart_str temp = {0};

	if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &post_data) == FAILURE){
		RETURN_FALSE;
	}

	data = zend_read_property(FETCH_THIS, ZEND_STRL("data"), 0 TSRMLS_CC);

	if(Z_TYPE_P(post_data) != IS_ARRAY){
		zend_error(E_WARNING, "post data must be array.");
		RETURN_FALSE;
	}

	post_data_ht = Z_ARRVAL_P(post_data);
	smart_str_appendl(&temp, Z_STRVAL_P(data), Z_STRLEN_P(data));

	for(zend_hash_internal_pointer_reset(post_data_ht);
		zend_hash_has_more_elements(post_data_ht) == SUCCESS;
		zend_hash_move_forward(post_data_ht)){
		zval **current_data;
		char *key;
		uint key_len;
		ulong idx;

		if(zend_hash_get_current_key_ex(post_data_ht, &key, &key_len, &idx, 0, NULL) != HASH_KEY_IS_STRING){
			continue;
		}

		if(zend_hash_get_current_data(post_data_ht, (void**)&current_data) == FAILURE){
			continue;
		}

		convert_to_string(*current_data);

		smart_str_appendl(&temp, key, key_len-1);
		smart_str_appendc(&temp, '=');
		smart_str_appendl(&temp, Z_STRVAL_PP(current_data), Z_STRLEN_PP(current_data));
		smart_str_appendc(&temp, '&');
	}

	smart_str_0(&temp);
	zend_update_property_stringl(FETCH_THIS, ZEND_STRL("data"), temp.c, temp.len TSRMLS_CC);
}

ZEND_METHOD(ext_fetch_url, setReadTimeout){
	zval *read_timeout;

	if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &read_timeout) == FAILURE){
		RETURN_FALSE;
	}

	if(Z_TYPE_P(read_timeout) != IS_LONG){
		zend_error(E_WARNING, "readtimeout must be integer.");
		RETURN_FALSE;
	}

	zend_update_property_long(FETCH_THIS, ZEND_STRL("read_timeout"), Z_LVAL_P(read_timeout) TSRMLS_CC);
}

ZEND_METHOD(ext_fetch_url, __destruct){}

static zend_function_entry ext_fetch_url_method[] = {
	ZEND_ME(ext_fetch_url, __construct, void_arginfo, ZEND_ACC_CTOR|ZEND_ACC_PUBLIC)
	ZEND_ME(ext_fetch_url, setBinary, setBinary_arginfo, ZEND_ACC_PUBLIC)
	ZEND_ME(ext_fetch_url, setFile, setFile_arginfo, ZEND_ACC_PUBLIC)
	ZEND_ME(ext_fetch_url, body, void_arginfo, ZEND_ACC_PUBLIC)
	ZEND_ME(ext_fetch_url, clean, void_arginfo, ZEND_ACC_PUBLIC)
	ZEND_ME(ext_fetch_url, errmsg, void_arginfo, ZEND_ACC_PUBLIC)
	ZEND_ME(ext_fetch_url, errcode, void_arginfo, ZEND_ACC_PUBLIC)
	ZEND_ME(ext_fetch_url, fetch, fetch_arginfo, ZEND_ACC_PUBLIC)
	ZEND_ME(ext_fetch_url, httpCode, void_arginfo, ZEND_ACC_PUBLIC)
	ZEND_ME(ext_fetch_url, responseCookies, responseCookies_arginfo, ZEND_ACC_PUBLIC)
	ZEND_ME(ext_fetch_url, responseHeaders, responseHeaders_arginfo, ZEND_ACC_PUBLIC)
	ZEND_ME(ext_fetch_url, setAllowRedirect, setAllowRedirect_arginfo, ZEND_ACC_PUBLIC)
	ZEND_ME(ext_fetch_url, setConnectTimeout, setConnectTimeout_arginfo, ZEND_ACC_PUBLIC)
	ZEND_ME(ext_fetch_url, setCookie, setCookie_arginfo, ZEND_ACC_PUBLIC)
	ZEND_ME(ext_fetch_url, setCookies, setCookies_arginfo, ZEND_ACC_PUBLIC)
	ZEND_ME(ext_fetch_url, setHeader, setHeader_arginfo, ZEND_ACC_PUBLIC)
	ZEND_ME(ext_fetch_url, setMethod, setMethod_arginfo, ZEND_ACC_PUBLIC)
	ZEND_ME(ext_fetch_url, setPostData, setPostData, ZEND_ACC_PUBLIC)
	ZEND_ME(ext_fetch_url, setReadTimeout, setReadTimeout_arginfo, ZEND_ACC_PUBLIC)
	ZEND_ME(ext_fetch_url, __destruct, void_arginfo, ZEND_ACC_DTOR|ZEND_ACC_PUBLIC)
	{NULL, NULL, NULL}
};

/* {{{ PHP_MINIT_FUNCTION
 */
EXT_STARTUP_FUNCTION(fetch_url)
{
	/* If you have INI entries, uncomment these lines 
	REGISTER_INI_ENTRIES();
	*/
	zend_class_entry fetch_ce;
	INIT_CLASS_ENTRY(fetch_ce, FETCH_CLASS_NAME, ext_fetch_url_method);

	ext_fetch_url_ce = zend_register_internal_class(&fetch_ce TSRMLS_CC);
	zend_declare_property_stringl(ext_fetch_url_ce, ZEND_STRL("body"), ZEND_STRL(""), ZEND_ACC_PROTECTED TSRMLS_CC);
	zend_declare_property_null(ext_fetch_url_ce, ZEND_STRL("errmsg"), ZEND_ACC_PROTECTED TSRMLS_CC);
	zend_declare_property_long(ext_fetch_url_ce, ZEND_STRL("errno"), 0, ZEND_ACC_PROTECTED TSRMLS_CC);
	zend_declare_property_null(ext_fetch_url_ce, ZEND_STRL("httpCode"), ZEND_ACC_PROTECTED TSRMLS_CC);
	zend_declare_property_stringl(ext_fetch_url_ce, ZEND_STRL("cookies"), ZEND_STRL(""), ZEND_ACC_PROTECTED TSRMLS_CC);
	zend_declare_property_stringl(ext_fetch_url_ce, ZEND_STRL("headers"), ZEND_STRL(""), ZEND_ACC_PROTECTED TSRMLS_CC);
	zend_declare_property_null(ext_fetch_url_ce, ZEND_STRL("send_headers"), ZEND_ACC_PROTECTED TSRMLS_CC);
	zend_declare_property_long(ext_fetch_url_ce, ZEND_STRL("allow_redirect"), 1, ZEND_ACC_PROTECTED TSRMLS_CC);
	zend_declare_property_long(ext_fetch_url_ce, ZEND_STRL("connect_timeout"), 5, ZEND_ACC_PROTECTED TSRMLS_CC);
	zend_declare_property_stringl(ext_fetch_url_ce, ZEND_STRL("method"), ZEND_STRL("get"), ZEND_ACC_PROTECTED TSRMLS_CC);
	zend_declare_property_long(ext_fetch_url_ce, ZEND_STRL("multilpart"), 0, ZEND_ACC_PROTECTED TSRMLS_CC);
	zend_declare_property_stringl(ext_fetch_url_ce, ZEND_STRL("data"), ZEND_STRL(""), ZEND_ACC_PROTECTED TSRMLS_CC);
	zend_declare_property_long(ext_fetch_url_ce, ZEND_STRL("read_timeout"), 60, ZEND_ACC_PROTECTED TSRMLS_CC);
	zend_declare_property_null(ext_fetch_url_ce, ZEND_STRL("binary_data"), ZEND_ACC_PROTECTED TSRMLS_CC);
	zend_declare_property_null(ext_fetch_url_ce, ZEND_STRL("upload_filepaths"), ZEND_ACC_PROTECTED TSRMLS_CC);
	return SUCCESS;
}
/* }}} */


/*
 * Local variables:
 * tab-width: 4
 * c-basic-offset: 4
 * End:
 * vim600: noet sw=4 ts=4 fdm=marker
 * vim<600: noet sw=4 ts=4
 */
